/*
 * Reports.cpp
 *
 *  Created on: Oct 19, 2011
 *      Author: kimi
 */

#include "Reports.h"

Reports::Reports(Properties * _props) {
	this->props = _props;
}

Reports::~Reports() {
}

bool Reports::encriptarArchivo(string filePath){
	bool res = false;

	Vigenere* vigenere = new Vigenere(props);
	
	string filePathE = filePath + "_vigenere";
	res = vigenere->encrypt(filePath,filePathE);
	
	if(remove(filePath.c_str()) != 0) {
		cerr << "No se pudo borrar el reporte." << endl;
		res = false;
	}
	delete vigenere;

	res = true;
	return res;
}

bool Reports::desencriptarArchivo(string filePath){
	bool res = false;

	Vigenere* vigenere = new Vigenere(props);
	
	string filePathDe = filePath + "2plain";
	res = vigenere->decrypt(filePath,filePathDe);
	
	delete vigenere;

	res = true;
	return res;
}

unsigned long Reports::contarVotos(vector<Count> datos) {
	unsigned long total = 0;

	if (datos.size() != 0) {
		for (int i = 0; i < datos.size(); i++) {
			Count datosCount = datos.at(i);
			total += datosCount.getCount();
		}
	}
	return total;
}

unsigned long Reports::getListaGanadora(vector<unsigned long> vectorVotosLista, vector<unsigned long> listas){
	unsigned long cantVotosGanador = 0;	
	unsigned long listaGanadora = 0;
	
	if(listas.size() != 0){
		cantVotosGanador = vectorVotosLista.at(0);
		listaGanadora = listas.at(0);
		
		for(int i=0; i<listas.size(); i++){
			if(cantVotosGanador < vectorVotosLista.at(i)){
				cantVotosGanador = vectorVotosLista.at(i);
				listaGanadora = listas.at(i);
			}
		}
	}	
	return listaGanadora;	
}

unsigned long Reports::contarVotosEleccion(vector<unsigned long> vectorVotos){
	unsigned long total = 0;
	
	for(int i=0; i<vectorVotos.size(); i++){
		total += vectorVotos[i];
	}
	
	return total;
}

unsigned long Reports::contarVotosLista(vector<unsigned long> listas,
		CountingMgr* cMgr) {
	int total = 0;
	int t = listas.size();
	for (unsigned long i = 0; i < listas.size(); i++) {
		unsigned long listaId = listas.at(i);
		vector<Count> conteo = cMgr->getCountFilteredByLista(listaId);

		total += contarVotos(conteo);
	}
	return total;
}

vector<int> Reports::contarVotosDistrito(vector<Count> datos) {
	vector<int> datosParciales;
	vector<int> auxVotos;
	int cantidadVotos = 0;
	vector<int> auxElecciones;
	int cantidadElecciones = 0;
	Count datosCount = datos.at(0);

	if (datos.size() != 0) {
		unsigned long idEleccion = datosCount.getEleccionId();
		for (int i = 0; i < datos.size(); i++) {
			datosCount = datos.at(i);

			if (idEleccion == datosCount.getEleccionId()) {
				cantidadVotos += datosCount.getCount();
				cantidadElecciones += 1;
			} else {
				auxVotos.push_back(cantidadVotos);
				auxElecciones.push_back(cantidadElecciones);

				cantidadElecciones = 1;
				cantidadVotos = datosCount.getCount();
				idEleccion = datosCount.getEleccionId();
			}
		}
		auxVotos.push_back(cantidadVotos);
		auxElecciones.push_back(cantidadElecciones);

		for (int i = 0; i < auxElecciones.size(); i++) {
			for (int j = 0; j < auxElecciones.at(i); j++) {
				datosParciales.push_back(auxVotos[i]);
			}
		}
	}
	return datosParciales;
}

bool Reports::createReportForEleccion(unsigned long idEleccion, string filePath,
		Properties * props) {
	bool res = false;
	char* data = NULL;
	unsigned long data_len = 0;
	unsigned long votosTotales = 0;

	ExtensibleHash* archivoListas = NULL;
	Lista* listaP = new Lista();
	string strL = props->get("workingPath") + props->get("ArchivoListas");
	archivoListas = new ExtensibleHash(strL,
			props->getInt("ArchivoListasBlockSize"));

	ExtensibleHash* archivoElecciones = NULL;
	Eleccion* eleccionP = new Eleccion();
	string strE = props->get("workingPath") + props->get("ArchivoElecciones");
	archivoElecciones = new ExtensibleHash(strE,
			props->getInt("ArchivoEleccionesBlockSize"));

	BTree* archivoCargos = NULL;
	Cargo* cargoP = new Cargo();
	string strC = props->get("workingPath") + props->get("ArchivoCargos");
	archivoCargos = new BTree(strC, props->getInt("ArchivoCargosBlockSize"));

	fstream _file;
	_file.open(filePath.c_str(), ios::out | ios::in | ios::trunc);

	if (!_file.is_open())
		throw ios_base::failure("cannot create file at \"" + filePath + "\".");

	CountingMgr* cMgr = new CountingMgr(
			props->get("workingPath") + props->get("ArchivoDeConteo"));

	vector<Count> vectorCount;

	vectorCount = cMgr->getCountFilteredByEleccion(idEleccion);

	if (vectorCount.size() != 0) {
		Count datosCount = vectorCount.at(0);

		if (archivoElecciones->get(idEleccion, &data, &data_len) == true) {
			eleccionP->unpack(data, data_len);
			_file << "Eleccion: " << eleccionP->getFecha() << endl;
			_file << "Cargo: " << eleccionP->getCargoPrincipalId();

			data = NULL;
			data_len = 0;
			archivoCargos->get(eleccionP->getCargoPrincipalId(), &data,
					&data_len);
			cargoP->unpack(data, data_len);
			_file << " " << cargoP->getCargo() << endl;
		}
		_file << endl;

		vector<unsigned long> listas;
		vector<unsigned long> vectorVotosLista;
				
		unsigned long idDistrito = datosCount.getDistritoId();
		unsigned long votosLista =0;
		unsigned long idLista =0;
		int posDesde = 0;
					
		for(int i=0; i<vectorCount.size(); i++){
			datosCount = vectorCount.at(i);
			if(idDistrito == datosCount.getDistritoId()){
				listas.push_back(datosCount.getListaId());
				vectorVotosLista.push_back(datosCount.getCount());
				posDesde += 1;
			}
			else{
				i=vectorCount.size();
				idDistrito = datosCount.getDistritoId();
			}
		}
						
		for(int i=posDesde; i<vectorCount.size(); i++){
			datosCount = vectorCount.at(i);
			
			for(int j=0; j<listas.size(); j++){
				idLista = listas.at(j);
				if(idLista == datosCount.getListaId()){
					vectorVotosLista[j] += datosCount.getCount();
					j=listas.size();
				}
				else{				
					if(j==listas.size()){
						listas.push_back(idLista);
						vectorVotosLista.push_back(datosCount.getCount());
					}
				}
			}			
		}
		
		unsigned long votosTotales = contarVotosEleccion(vectorVotosLista);
		
		for(int i=0; i<listas.size();i++){		
			data = NULL;
			data_len = 0;			
			
			_file<<"Lista: "<<listas.at(i);
			if(archivoListas->get(listas.at(i),&data,&data_len) == true){
				listaP->unpack(data,data_len);
				_file<<" "<<listaP->getNombreLista();	
			}
			
		double porcentaje = 0;
		if(votosTotales != 0){
			porcentaje = 100* vectorVotosLista[i]/votosTotales;
		}
		_file<<" -> Votos: "<<porcentaje<<"% ("<<vectorVotosLista.at(i)<<" votos)"<<endl;
		res = true;
		}
		
		_file<<endl;
		_file<<"Total votos en la eleccion: "<<votosTotales<<endl;
		
		unsigned long ganador = getListaGanadora(vectorVotosLista, listas);
		
		data = NULL;
		data_len = 0;			
				
		if(archivoListas->get(ganador,&data,&data_len) == true){
			listaP->unpack(data,data_len);
			_file<<endl;
			_file<<"Lista ganadora: "<<ganador<<" - "<<listaP->getNombreLista()<<endl;	
		}
	}
	else
		_file<<"No hay Resultados en la Eleccion "<<idEleccion<<endl;
	
	delete cMgr;
	_file.flush();
	_file.close();

	delete archivoCargos;
	delete archivoElecciones;
	delete archivoListas;

	encriptarArchivo(filePath);
	desencriptarArchivo(filePath+"_vigenere");
	
	return res;
}

bool Reports::createReportForLista(string nombreLista, string filePath,
		Properties * props) {
	bool res = false;
	fstream _file;
	unsigned long votosTotalesLista = 0;
	unsigned long votosTotalesEleccion = 0;

	_file.open(filePath.c_str(), ios::out | ios::in | ios::trunc);

	if (!_file.is_open())
		throw ios_base::failure("cannot create file at \"" + filePath + "\".");

	ExtensibleHash* archivoListas = NULL;
	string strL = props->get("workingPath") + props->get("ArchivoListas");
	archivoListas = new ExtensibleHash(strL,
			props->getInt("ArchivoListasBlockSize"));

	ExtensibleHash* archivoElecciones = NULL;
	string strE = props->get("workingPath") + props->get("ArchivoElecciones");
	archivoElecciones = new ExtensibleHash(strE,
			props->getInt("ArchivoEleccionesBlockSize"));

	BTree* archivoCargos = NULL;
	string strC = props->get("workingPath") + props->get("ArchivoCargos");
	archivoCargos = new BTree(strC, props->getInt("ArchivoCargosBlockSize"));

	BTree* archivoDistritos = NULL;
	string str = props->get("workingPath") + props->get("ArchivoDistritos");
	archivoDistritos = new BTree(str,
			props->getInt("ArchivoDistritosBlockSize"));

	CountingMgr* cMgr = new CountingMgr(
			props->get("workingPath") + props->get("ArchivoDeConteo"));

	vector<unsigned long> listasConIgualNombre = cMgr->getListasIDsParaLista(
			nombreLista);

	_file << "Lista: " << nombreLista << endl;
	_file << endl;

	votosTotalesLista = contarVotosLista(listasConIgualNombre, cMgr);

	for (unsigned long i = 0; i < listasConIgualNombre.size(); i++) {
		unsigned long listaId = listasConIgualNombre.at(i);
		vector<Count> conteo = cMgr->getCountFilteredByLista(listaId);

		votosTotalesEleccion = contarVotos(conteo);

		Lista *l = new Lista();
		Eleccion* e = new Eleccion();

		char* data = NULL;
		unsigned long data_len = 0;
		if (archivoListas->get(listaId, &data, &data_len) == false) {
			cerr << "Error al obtener información de la lista" << endl;
			continue;
		}
		l->unpack(data, data_len);
		free(data);
		data = NULL;

		if (archivoElecciones->get(l->getIdEleccion(), &data, &data_len)
				== false) {
			cerr << "Error al obtener información de la elección" << endl;
			continue;
		}
		e->unpack(data, data_len);
		free(data);
		data = NULL;

		_file << "Eleccion: " << e->getFecha() << " ";
		_file << "Cargo: " << e->getCargoPrincipalId();

		Cargo*c = new Cargo();
		if (archivoCargos->get(e->getCargoPrincipalId(), &data, &data_len)
				== false) {
			cerr << "Error al obtener información del cargo" << endl;
			continue;
		}
		c->unpack(data, data_len);
		free(data);
		data = NULL;

		_file << " - " << c->getCargo() << endl;

		for (unsigned j = 0; j < e->getDistritos().size(); j++) {

			unsigned long distritoId = e->getDistrito(j);

			unsigned long cantidadVotos = 0;
			for (unsigned k = 0; k < conteo.size(); k++) {
				if (conteo.at(k).getDistritoId() == distritoId) {
					cantidadVotos += conteo.at(k).getCount();
				}
			}

			Distrito *d = new Distrito();

			if (archivoDistritos->get(distritoId, &data, &data_len) == false) {
				cerr << "Error al obtener información del distrito" << endl;
			} else {
				d->unpack(data, data_len);

				char _dist_[64];
				char _votos_[64];
				sprintf(_dist_, "%lu", d->getDistritoId());

				int porcentaje = 0;
				if (votosTotalesEleccion != 0) {
					porcentaje = 100 * cantidadVotos / votosTotalesEleccion;
				}
				sprintf(_votos_, "%lu", cantidadVotos);
				_file << "Distrito " << _dist_ << " - " << d->getNombreDist()
						<< ": " << porcentaje << "% (" << _votos_ << " votos)"
						<< endl;

				free(data);
				data = NULL;
			}
			delete d;
		}
		int porcentaje = 0;

		if (votosTotalesLista != 0) {
			porcentaje = 100 * votosTotalesEleccion / votosTotalesLista;
		}
		_file << "Total Votos: " << porcentaje << "% (" << votosTotalesEleccion
				<< " votos)" << endl;
		_file << endl;

		delete c;
		delete e;
	}

	delete cMgr;
	_file << "Total Votos Lista: " << votosTotalesLista << endl;
	_file << endl;
	_file.flush();
	_file.close();

	delete archivoCargos;
	delete archivoDistritos;
	delete archivoElecciones;
	delete archivoListas;

	encriptarArchivo(filePath);
	desencriptarArchivo(filePath+"_vigenere");
	
	return res;
}

bool Reports::createReportForDistrito(unsigned long idDistrito, string filePath,
		Properties * props) {
	bool res = false;
	char* data = NULL;
	unsigned long data_len = 0;
	unsigned long votosTotalesDistrito = 0;
	vector<int> votosTotalesEleccion;

	BTree* archivoDistritos = NULL;
	Distrito* distritoP = new Distrito();
	string str = props->get("workingPath") + props->get("ArchivoDistritos");
	archivoDistritos = new BTree(str,
			props->getInt("ArchivoDistritosBlockSize"));

	ExtensibleHash* archivoListas = NULL;
	Lista* listaP = new Lista();
	string strL = props->get("workingPath") + props->get("ArchivoListas");
	archivoListas = new ExtensibleHash(strL,
			props->getInt("ArchivoListasBlockSize"));

	ExtensibleHash* archivoElecciones = NULL;
	Eleccion* eleccionP = new Eleccion();
	string strE = props->get("workingPath") + props->get("ArchivoElecciones");
	archivoElecciones = new ExtensibleHash(strE,
			props->getInt("ArchivoEleccionesBlockSize"));

	BTree* archivoCargos = NULL;
	Cargo* cargoP = new Cargo();
	string strC = props->get("workingPath") + props->get("ArchivoCargos");
	archivoCargos = new BTree(strC, props->getInt("ArchivoCargosBlockSize"));

	fstream _file;
	_file.open(filePath.c_str(), ios::out | ios::in | ios::trunc);

	if (!_file.is_open())
		throw ios_base::failure("cannot create file at \"" + filePath + "\".");

	CountingMgr* cMgr = new CountingMgr(
			props->get("workingPath") + props->get("ArchivoDeConteo"));

	vector<Count> vectorCount;
	vectorCount = cMgr->getCountFilteredByDistrito(idDistrito);

	if (vectorCount.size() != 0) {
		Count datosCount = vectorCount.at(0);

		votosTotalesDistrito = contarVotos(vectorCount);
		votosTotalesEleccion = contarVotosDistrito(vectorCount);

		if (archivoDistritos->get(idDistrito, &data, &data_len) == true) {
			distritoP->unpack(data, data_len);
			_file << "Distrito: " << idDistrito << " "
					<< distritoP->getNombreDist() << endl;
		}
		_file << endl;

		data = NULL;
		data_len = 0;
		unsigned long eleccionId = datosCount.getEleccionId();

		if (archivoElecciones->get(eleccionId, &data, &data_len) == true) {
			eleccionP->unpack(data, data_len);
			_file << "Eleccion: " << eleccionP->getFecha() << endl;
			_file << "Cargo: " << eleccionP->getCargoPrincipalId();

			data = NULL;
			data_len = 0;
			archivoCargos->get(eleccionP->getCargoPrincipalId(), &data,
					&data_len);
			cargoP->unpack(data, data_len);
			_file << " " << cargoP->getCargo() << endl;
		}

		for (int i = 0; i < vectorCount.size(); i++) {
			datosCount = vectorCount.at(i);

			if (eleccionId != datosCount.getEleccionId()) {
				eleccionId = datosCount.getEleccionId();
				_file << endl;

				int porcentajeTotalEleccion = 0;
				if (votosTotalesDistrito != 0) {
					porcentajeTotalEleccion = 100 * votosTotalesEleccion[i - 1]
							/ votosTotalesDistrito;
				}
				_file << "Total Votos: " << porcentajeTotalEleccion << "% ("
						<< votosTotalesEleccion[i - 1] << " votos)" << endl;
				_file << endl;

				data = NULL;
				data_len = 0;

				if (archivoElecciones->get(eleccionId, &data, &data_len)
						== true) {
					eleccionP->unpack(data, data_len);
					_file << "Eleccion: " << eleccionP->getFecha() << endl;
					_file << "Cargo: " << eleccionP->getCargoPrincipalId();

					data = NULL;
					data_len = 0;
					archivoCargos->get(eleccionP->getCargoPrincipalId(), &data,
							&data_len);
					cargoP->unpack(data, data_len);
					_file << " " << cargoP->getCargo() << endl;
				}
			}

			data = NULL;
			data_len = 0;
			if (archivoListas->get(datosCount.getListaId(), &data, &data_len)
					== true) {
				listaP->unpack(data, data_len);
				_file << "Lista: " << datosCount.getListaId() << " "
						<< listaP->getNombreLista();
			}

			int porcentaje = 0;
			if (votosTotalesEleccion[i] != 0) {
				porcentaje = 100 * datosCount.getCount()
						/ votosTotalesEleccion[i];
			}
			_file << " -> Votos: " << porcentaje << "%" << " ("
					<< datosCount.getCount() << " votos)" << endl;
		}
		res = true;
	} else
		_file << "No hay Resultados para el Distrito " << idDistrito << endl;

	_file << endl;

	int porcentajeTotalEleccion = 0;
	if (votosTotalesDistrito != 0) {
		porcentajeTotalEleccion = 100
				* votosTotalesEleccion[vectorCount.size() - 1]
				/ votosTotalesDistrito;
	}
	_file << "Total Votos: " << porcentajeTotalEleccion << "% ("
			<< votosTotalesEleccion[vectorCount.size() - 1] << " votos)"
			<< endl;
	_file << endl;
	_file << endl;
	_file << "Total Votos en el Distrito: " << votosTotalesDistrito << endl;
	delete cMgr;
	_file.flush();
	_file.close();

	delete archivoCargos;
	delete archivoDistritos;
	delete archivoElecciones;
	delete archivoListas;
	
	encriptarArchivo(filePath);
	desencriptarArchivo(filePath+"_vigenere");
	
	return res;
}
